package p.ithorns.tools.translator.apt;

import com.sun.source.tree.Tree;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.ListBuffer;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.reflections.util.ConfigurationBuilder;
import p.ithorns.tools.translator.apt.model.AnnotateMeta;
import p.ithorns.tools.translator.apt.model.AnnotatedParam;
import p.ithorns.tools.translator.apt.utils.AptUtil;

import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.stream.Collectors;

/**
 * AbstractHandler
 *
 * @author HtL
 * @date 2024/6/11 8:35
 * @since 1.0.0
 */
public abstract class AbstractHandler {

    private final static String APT_HANDLER_PACKAGE = AbstractHandler.class.getPackageName();

    private final static Map<String, AbstractHandler> MAP = new HashMap<>();

    static {
        // 扫包
        Reflections reflections = new Reflections(new ConfigurationBuilder()
                .forPackages(APT_HANDLER_PACKAGE)
                .addScanners(Scanners.SubTypes)
        );

        Set<Class<? extends AbstractHandler>> set = reflections.getSubTypesOf(AbstractHandler.class);
        for (Class<? extends AbstractHandler> subType : set) {
            try {
                Constructor<? extends AbstractHandler> constructor = subType.getDeclaredConstructor();
                AbstractHandler handler = constructor.newInstance();
                Set<String> handleTypes = handler.getHandleTypes();
                for (String handleType : handleTypes) {
                    MAP.put(handleType, handler);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static AbstractHandler get(String key) {
        return MAP.get(key);
    }

    static Class<?> getGenericType(Class<?> clazz) {
        // 获取直接继承的泛型参数类型
        ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericSuperclass();
        return (Class<?>) parameterizedType.getActualTypeArguments()[0];
    }

    public AnnotateMeta handle(AnnotateMeta meta) {
        if (!typeMatch(meta)) {
            return meta;
        }

        return rebuild(meta);
    }

    protected List<AnnotatedParam> cloneArgs(AnnotateMeta meta) {
        ListBuffer<JCTree.JCExpression> args = meta.getArgs();
        if (null == args || args.isEmpty()) {
            return Collections.emptyList();
        }

        return args.stream()
                .map(arg -> {
                    if (Tree.Kind.ANNOTATION_TYPE == arg.getKind()) {
                        JCTree.JCAssign assign = (JCTree.JCAssign) arg;
                        return new AnnotatedParam(assign.lhs.toString(), assign.rhs);
                    }
                    return null;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    protected Map<String, Object> cloneArgsMap(AnnotateMeta meta) {
        ListBuffer<JCTree.JCExpression> args = meta.getArgs();
        if (null == args || args.isEmpty()) {
            return Collections.emptyMap();
        }

        Map<String, Object> map = new HashMap<>();
        for (JCTree.JCExpression arg : args) {
            if (Tree.Kind.ASSIGNMENT == arg.getKind()) {
                JCTree.JCAssign assign = (JCTree.JCAssign) arg;
                map.put(assign.lhs.toString(), assign.rhs);
            }
        }
        return map;
    }

    private boolean typeMatch(AnnotateMeta meta) {
        Set<String> handleTypes = getHandleTypes();
        if (null == handleTypes || handleTypes.isEmpty()) {
            return false;
        }
        return handleTypes.contains(meta.getFqn());
    }

    protected abstract AnnotateMeta rebuild(AnnotateMeta meta);

    protected abstract Set<String> getHandleTypes();

}